/*
 test change
key commands -- create some for page forward/back
*/



Ripple{
	
	classvar currentSL, <server, <>mainWindow, <voices, <pages, connectionManager, <>onLaunch, <>settings, alertWin;
	
	*init{|quickStart = false|
	
			settings = RippleSettings.new;
			server = Server.default;
			
			RippleButtonBlinker.start;
			
			server.options.numAudioBusChannels = 512;
			server.options.memSize = 102400;
			server.options.numWireBufs = 128;
			
			server.doWhenBooted({
			
				var c;
				
				"".postln;
				"".postln;
				"INITIALIZING RIPPLE. ONE MOMENT PLEASE.".postln;
				
				server.recSampleFormat = "int16";

				
				if(quickStart.not){
					this.loadSynthDefs;
				};
				
				RippleMidiManager.init;
				PulseManager.init;
						
				voices = OrderedEnvir.new;
				connectionManager = ConnectionManager.new(this);
				
				
				c = Condition.new(false);
				
				AppClock.sched(0,{
					//	mainWindow = RippleGui.init;
					//mainWindow.setParent(this);
				
					mainWindow = RippleGui2.new(this);
					pages = List.new;
					mainWindow.numPages.do({
						pages.add(List.new);
					});
					c.test = true;
					c.signal;
				});
				
				fork{
					
					c.wait;

					try{
						Ripple.onLaunch.value;
						
					}{|e| 
						"exception in Ripple.onLaunch".postln;
						"Receiver: ".post;
						e.receiver.postln;
						e.errorString.postln;
						e.reportError;
						
						
					};
				};				

			});
		
			server.boot;
			
	}
	
	*getMainWindowBounds{
		^mainWindow.bounds;
	}
	
	*loadSynthDefs{
		BaseSynthDescriptor.loadAllSynths;
		RippleFX.loadAllSynths;
		if(Animal.notNil){
			Animal.subclasses.do{|theClass|
				theClass.writeSynthDef;
			}
		};
	}
	
	*running{
		if(voices != nil){
			^true;
		};
		^false;
	}
	
	*drawVoiceDivider{
		mainWindow.drawVoiceDivider;
	}
	
	*getSelectedPath{
		^mainWindow.getSelectedFilePath;	
	}
	
	*getTempo{
		BeatManager.getTempo;	
	}
		
	*setBPM{|val|
		BeatManager.setBPM;
	}
		
	*setCurrentSL{|theSL|
			currentSL = theSL;	
	}
	
	*playSound{|voiceName, pitch, pitchRatio, volume|
		voices.at(voiceName.asSymbol).playSound(pitchRatio: pitchRatio, volume: volume, pitch: pitch);	
	}
	
	*soundPlayed{|voiceName, time, sustain = false|
		if(mainWindow == nil){
			^nil;
		};
		if(connectionManager != nil){
			connectionManager.soundPlayed(voiceName);			};
		mainWindow.soundPlayed(voiceName, time, sustain);	
	}
	
	*launchSL{| synthType|
		var path; 
		path = this.getSelectedPath;
		this.launchSLFromPath(synthType, path);

		
	}

	*launchSLFromPath{| synthType, path, name|
		var voice;
		
//		try{
		
			voice = RippleVoiceFactory.buildVoice(path, synthType, name);
			this.registerVoice(voice);
//		}{|error| 
//			if(
//				error.species.name == 'InvalidSoundFileError',
//				{error.what.postln;},
//				{error.throw}
//			);	
//		};
		
		^voice;
	}
	
	*addConnection{|sourceName, targetName, connectionType|
		connectionManager.addConnection(sourceName, targetName, connectionType);
	}
	
	*commenceHitch{|name|
		connectionManager.commenceHitch(name);	
	}
	
	*freeAllHarnessed{|name|
		connectionManager.freeAllHarnessed(name);
	}
	
	*setControlValue{|voiceName, controlName, value|
		voices.at(voiceName.asSymbol).setControlValue(controlName, value);
	}
	
	*getActiveVoiceNames{
		var voiceNames;
	
		voiceNames = List.new;
		
		voices.do{|voice|
			voiceNames.add(voice.getName.asSymbol);	
		}
	
		^voiceNames;
	}
	
	*getVoices{
		^voices;	
	}
	
	*voiceExists{|name|
	
	
		if(voices == nil){
			^false;
		};
	
		if(voices.at(name.asSymbol) == nil){
			^false;
		};
		^true;
	}
	
	*tooManyVoices{
		var doOpen = false;
		if(alertWin == nil){
			doOpen = true;
		};
		if(alertWin.notNil){
			if(alertWin.isClosed){
				doOpen = true;
			}
		};
			
		if(doOpen){
			{
			alertWin = GUIUtils.popupWindow;
			SCStaticText(alertWin, 300@200)
				.string_("Warning! Ripple is using too much CPU power. You can fix this by running fewer synths, shortening the duration (attack + decay + release) of the synths, or reducing the triggerFreq value of some of your synths.");
			SCButton(alertWin, 50@20)
				.states_([["OK", Color.black, Color.white]])
				.action_({alertWin.parent.close});
			}.defer;

		};
		
		10.do{
			"####################################".postln;
		}	
	}
	
	*selectVoice{|voice|
		currentSL = voice;
		if(voice.isGuiOpen.not){
			voice.displayGui	
		}
	}
	
	*getVoice{|voiceName|
		^voices.at(voiceName.asSymbol);
	}
	
	*getCurrentPageVoicesByMidiChannel{|chan|
		var midiChanVoices, pageVoices;
		midiChanVoices = this.getVoicesByMidiChannel(chan);
		pageVoices = pages[mainWindow.activeVoicePage];
		midiChanVoices.do({|voice|
			if(pageVoices.includes(voice).not){
				midiChanVoices.remove(voice);	
			}
		});
		^midiChanVoices;
	}

	*getVoicesByMidiChannel{|channel|
		var voicesByChannel;
		voicesByChannel = List.new;
		voices.do	{|voice|
			if(voice.getMidiChannel == channel){
				voicesByChannel.add(voice);	
			}
		};
		^voicesByChannel;
	}
	
	*reset{
		this.resetVoices;
	}
	
	*resetVoices{
		voices.do{|voice|
			voice.reset;
		}	
	}
	
	*accept{|xb|
		var childXb;
		xb.forRipple(this);
		
		pages.do({|page|
			var pageXb;
			pageXb = XMLNodeBuilder.new;
			pageXb.setParent(xb.getNode);
			pageXb.forPage(page);
			page.do{|voice|
				var childXb;
				childXb = XMLNodeBuilder.new;
				childXb.setParent(pageXb.getNode);
				voice.accept(childXb);
			};
		});

			
			
		
		childXb = XMLNodeBuilder.new;
		childXb.setParent(xb.getNode);
		connectionManager.accept(childXb);
		
	}
	

	*prevPage{
		mainWindow.activeVoicePage_( mainWindow.activeVoicePage - 1 );	
	}
	
	*nextPage{
		mainWindow.activeVoicePage_( mainWindow.activeVoicePage + 1 );	
	}
	
	*setVoicePage{|index|
		mainWindow.activeVoicePage_(index);	
	}
	
	*registerVoice{|voice|
		voices.put(voice.getName.asSymbol, voice);
		pages[mainWindow.activeVoicePage].add(voice);
												pages.postln;
		{
			mainWindow.registerVoice(voice);
		}.defer;
	}
	
	*removeVoice{|voiceName|
		var voice;
		voice = Ripple.getVoice(voiceName);
		pages[mainWindow.activeVoicePage].remove(voice);
		mainWindow.deRegisterVoice(voice);
		voices.removeAt(voiceName.asSymbol);
		voices.envir.removeAt(voiceName.asSymbol);
	}
	
	*getActiveVoice{
		^currentSL;
	}
	
	*setActiveVoice{|voice|
		currentSL = voice;
	}
	
	*currentSL{
		^currentSL;
	}
	
}

R : Ripple{

}

RippleSettings{
	classvar <>settingsLoc;
	var settingsDict;	
	
	*new{
		^super.new.init;
	}
	
	*initClass{
		settingsLoc = "preferences/RippleSettings";
	}
	
	init{
		if(Object.readArchive(settingsLoc).isNil, {
			settingsDict = IdentityDictionary.new;
		}, {
			settingsDict = Object.readArchive(settingsLoc)
		});	
	}
		
	set{|key, val|
		settingsDict.add(key.asSymbol -> val);
		settingsDict.writeArchive(settingsLoc);
	}
	
	setIfNull{|key, val|
		if(this.get(key) == nil){
			this.set(key,val);
		}
	}
	
	*setIfNull{|key, val|
		RippleSettings.new.setIfNull(key, val);	
		
	}
	
	
	get{|key|
		^settingsDict.at(key.asSymbol);
	}
}

















